#pragma once
#include "Vector3.hpp"
#include "Array.hpp"

namespace zzz {
template <typename T>
struct Array<3, T> : public ArrayBase<3, T>
{
  using ArrayBase<3, T>::v;
  using ArrayBase<3, T>::subsizes;
public:
  //constructor
  Array(void){}
  Array(const zuint x, const zuint y, const zuint z):ArrayBase<3, T>(Vector3ui(x, y, z)){}
  explicit Array(const VectorBase<3,zuint> &size):ArrayBase<3, T>(size){}
  Array(const T *data, const zuint x, const zuint y, const zuint z):ArrayBase<3, T>(data, Vector3ui(x, y, z)){}
  Array(const T *data, const VectorBase<3, zuint> &size):ArrayBase<3, T>(data, size){}
  Array(const Array<3, T>& a):ArrayBase<3, T>(a) {}
  Array(const ArrayBase<3, T>& a):ArrayBase<3, T>(a) {}

  //assign
  inline const Array<3, T> &operator=(const Array<3, T>& a){ArrayBase<3, T>::operator =(a); return *this;}
  using ArrayBase<3, T>::operator=;

  //reference
  using ArrayBase<3, T>::operator();
  using ArrayBase<3, T>::At;
  const T& At(const zuint x, const zuint y, const zuint z) const{
    ZRCHECK_LT(x, Size(0));
    ZRCHECK_LT(y, Size(1));
    ZRCHECK_LT(z, Size(2));
    return At(ToIndex(x, y, z));
  }
  T& At(const zuint x, const zuint y, const zuint z) {
    ZRCHECK_LT(x, Size(0));
    ZRCHECK_LT(y, Size(1));
    ZRCHECK_LT(z, Size(2));
    return At(ToIndex(x, y, z));
  }
  const T& operator()(const zuint x, const zuint y, const zuint z) const{return At(x, y, z);}
  T& operator()(const zuint x, const zuint y, const zuint z) {return At(x, y, z);}

  T Interpolate(const double x, const double y, const double c) const {return ArrayBase<3, T>::Interpolate(Vector3d(x, y, z));}
  using ArrayBase<3, T>::Interpolate;

  zuint ToIndex(const zuint x, const zuint y, const zuint z) const {return x*subsizes[0] + y*subsizes[1] + z*subsizes[2];}
  using ArrayBase<3, T>::ToIndex;

  bool CheckIndex(const zuint x, const zuint y, const zuint z) const {return ArrayBase<3, T>::CheckIndex(Vector3ui(x, y, z));}
  using ArrayBase<3, T>::CheckIndex;

  void SetSize(const zuint x, const zuint y, const zuint z) {ArrayBase<3, T>::SetSize(Vector3ui(x, y, z));}
  using ArrayBase<3, T>::SetSize;

  void Fill(const zuint x, const zuint y, const zuint z, const T &a) {
    SetSize(x, y, z);
    Fill(a);
  }
  using ArrayBase<3, T>::Fill;

};

typedef Array<3,zint8> Array3i8;
typedef Array<3,zuint8> Array3ui8;
typedef Array<3,zint16> Array3i16;
typedef Array<3,zuint8> Array3ui16;
typedef Array<3,zint32> Array3i32;
typedef Array<3,zuint32> Array3ui32;
typedef Array<3,zint64> Array3i64;
typedef Array<3,zuint64> Array3ui64;
typedef Array<3,zfloat32> Array3f32;
typedef Array<3,zfloat64> Array3f64;

typedef Array<3,char> Array3c;
typedef Array<3,zuchar> Array3uc;
typedef Array<3,short> Array3s;
typedef Array<3,zushort> Array3us;
typedef Array<3,int> Array3i;
typedef Array<3,zuint> Array3ui;
typedef Array<3,float> Array3f;
typedef Array<3,double> Array3d;
};  // namespace zzz